package com.google.code.pdfbrick.engine.command;

import java.util.Collections;
import java.util.List;

import com.google.code.pdfbrick.engine.constant.CommandConstant;
import com.google.code.pdfbrick.engine.exception.CommandParseException;

/**
 * @author Johny Huang
 * @date 2009-3-12
 */
public class FilePageRangeParser {
	
	private static final FilePageRangeParser fprParser=new FilePageRangeParser();
	
	public static final boolean IS_OPTIMIZED=false;
	
	public static final boolean IS_SORT_PAGE_INDEX_ASC=false;

	private FilePageRangeParser(){
		
	}
	
	public static FilePageRangeParser getInstance(){
		return fprParser;
	}
	
	/**
	 * @param filePageRangeStr
	 * @return the object which contain the file path and the page indexes information.
	 * 
	 * The command string should have a patter like 'filePath','filePath<startPageIndex-endPageIndex>' or 'filePath<pageIndex1,pageIndex2>'.
	 * 
	 * For example:
	 *     command string: "file:/c:/test.pdf<1-6,9>"   
	 *     after parsed:
	 *     
	 *     FilePageRange{
	 *     filePath->file:/c:/test.pdf
	 *     pageRangeList->{PageRange{
	 *     					 startIndex->1,
	 *     					 endIndex->6
	 *     					},
	 *     					PageRange{
	 *     					 startIndex->9,
	 *     					 endIndex->9
	 *     					}}
	 *     }
	 *     
	 *  
	 */
	public FilePageRange parseFilePageRangeStr(final String filePageRangeStr){
		if (filePageRangeStr == null) {
			throw new NullPointerException("incomplete command");
		}

		FilePageRange fpr = new FilePageRange();
		int stIndex = filePageRangeStr.indexOf(CommandConstant.LT);
		int ltIndex = filePageRangeStr.indexOf(CommandConstant.MT);

		if (ltIndex == -1 && stIndex == -1) {
			//If there is no '<' or '>' in command,it should be a single page number.
			fpr.setFilePath(filePageRangeStr);
			fpr.setAllPages(true);
			return fpr;
		}

		if (stIndex != -1 && ltIndex != -1) {
			//If both '<' and '>' exist, it should be a range of pages.
			fpr.setFilePath(filePageRangeStr.substring(0,stIndex));
			String pageStr=filePageRangeStr.substring(stIndex+1, ltIndex);
			String[] pageRangeStrs=pageStr.split(CommandConstant.COMMA);
			for(String pageRangeStr:pageRangeStrs){
				fpr.addPageRange(parsePageRange(pageRangeStr));
			}			
		}else{
			throw new CommandParseException("Incomplete command:'<' and '>' must exist in pairs.");
		}
		
		if(IS_SORT_PAGE_INDEX_ASC){
			fpr.setPageRangeList(sortPageIndexAsc(fpr.getPageRangeList()));
		}
		
		if(IS_OPTIMIZED && IS_SORT_PAGE_INDEX_ASC){
			fpr.setPageRangeList(sortAndMergePageRange(fpr.getPageRangeList()));
		}

		return fpr;
	}
	
	private PageRange parsePageRange(String pageRangeStr){
		if(pageRangeStr==null){
			throw new NullPointerException("incomplete command");
		}
		
		PageRange pr=new PageRange();		
		int minusIndex=pageRangeStr.indexOf(CommandConstant.MINUS);
		
		if(minusIndex==-1){
			//If it's a single page.
			try{
			pr.setSinglePage(Integer.valueOf(pageRangeStr).intValue());
			}catch(NumberFormatException nfe){
				throw new CommandParseException("Invalid page numbers",nfe);
			}
		}else{		
			//If it's a range of pages.
			String[] indexes=pageRangeStr.split(CommandConstant.MINUS);
			if(indexes.length<2){
				throw new CommandParseException("Incomplete command:page range must be writen in '<startIndex-endIndex>' pattern.");
			}else{
				int startIndex,endIndex;
				
				try{
					startIndex=Integer.valueOf(indexes[0]).intValue();
					endIndex=Integer.valueOf(indexes[1]).intValue();
				}catch(NumberFormatException nfe){
					throw new CommandParseException("Invalid page numbers",nfe);
				}
				
				pr.setStartIndex(startIndex);
				pr.setEndIndex(endIndex);
				
			}
		}		
		
		return pr;
	}
	
	private List<PageRange> sortPageIndexAsc(List<PageRange> pageRangeList){
		List<PageRange> prList=pageRangeList;
		int listSize=prList.size();
		int temp;
		if(listSize>1){
			for(PageRange pr:prList){
				if(pr.getStartIndex()>pr.getEndIndex()){
					temp=pr.getStartIndex();
					pr.setStartIndex(pr.getEndIndex());
					pr.setEndIndex(temp);
				}
			}
		}
		return prList;
	}
	
	private List<PageRange> sortAndMergePageRange(List<PageRange> pageRangeList){		
		if(pageRangeList==null){
			return null;
		}	
		
		int listSize=pageRangeList.size();
		int startIndexFlag=0;
		PageRange cur,next;
			
		if(listSize>1){
			Collections.sort(pageRangeList);
			
			int listSize_1=listSize-1;
			
			
			for(int i=0;i<listSize;i++){
				if(i==listSize_1){
					break;
				}				
				cur=pageRangeList.get(i);
				next=pageRangeList.get(i+1);
				
				if(cur.getEndIndex()>=next.getStartIndex())
				{
					if(cur.getEndIndex()>=next.getEndIndex()){						
						pageRangeList.set(i+1, cur);
						startIndexFlag++;
					}else{
						cur.setEndIndex(next.getEndIndex());
						pageRangeList.set(i+1, cur);
						startIndexFlag++;
					}
				}
				//TODO consider the situation cur.getEndIndex()=next.getStartIndex()-1
			}
		}
				
		return pageRangeList.subList(startIndexFlag, listSize);
	}
	
	

}
